home *** CD-ROM | disk | FTP | other *** search
/ Skunkware 5 / Skunkware 5.iso / src / X11 / xconq / map.c < prev    next >
C/C++ Source or Header  |  1995-05-09  |  29KB  |  1,090 lines

  1. /* Copyright (c) 1987, 1988  Stanley T. Shebs, University of Utah. */
  2. /* This program may be used, copied, modified, and redistributed freely */
  3. /* for noncommercial purposes, so long as this notice remains intact. */
  4.  
  5. #pragma comment(exestr, "@(#) map.c 12.1 95/05/09 ")
  6.  
  7. /* RCS $Header: map.c,v 1.4 88/07/20 14:29:55 shebs Exp $ */
  8.  
  9. /* Code relating to the reading and writing of mapfiles.  A mapfile comes */
  10. /* in several independent sections, each of whose presence is flagged in the */
  11. /* header line.  A mapfile may also specify the inclusion of other mapfiles. */
  12.  
  13. #include "config.h"
  14. #include "misc.h"
  15. #include "dir.h"
  16. #include "period.h"
  17. #include "side.h"
  18. #include "unit.h"
  19. #include "map.h"
  20. #include "global.h"
  21.  
  22. /* A mapfile header has various pieces that we need to save. */
  23.  
  24. typedef struct a_header {
  25.     int numincls;               /* number of included files */
  26.     char *includes[MAXINCLUDES];  /* raw names of included files */
  27.     int numnotes;               /* number of lines in designer notes */
  28.     char *notes[MAXMAPNOTES];   /* designer notes for mapfile */
  29.     char *sections;             /* +/- string of mapfile sections */
  30.     int sdetail;
  31.     int udetail;
  32. } Header;
  33.  
  34. /* Sections of a mapfile have their recognizers. */
  35.  
  36. #define vsect(s) (s[0] == '+')
  37. #define psect(s) (s[1] == '+')
  38. #define msect(s) (s[2] == '+')
  39. #define gsect(s) (s[3] == '+')
  40. #define ssect(s) (s[4] == '+')
  41. #define usect(s) (s[5] == '+')
  42.  
  43. /* These vars tell init code what it has to do itself. */
  44.  
  45. extern bool periodmade, mapmade, globalsmade, sidesmade, unitsmade;
  46.  
  47. extern int nextid;
  48.  
  49. World world;                    /* the world structure itself! */
  50.  
  51. Header *mainhead = NULL;        /* the "top-level" map header - needs saving */
  52.  
  53. int nummaps;                    /* Number of mapfiles loaded */
  54.  
  55. int curgiven;                   /* tmp for side creation */
  56. int nux, nuy;                   /* where to put a newly created unit */
  57. int nusn;                       /* Number of unit's side */
  58. int nut;                        /* Number of the Unit Transport */
  59.  
  60. char *mapfilenames[MAXLOADED];  /* Names of loaded files */
  61. char *perfilename = NULL;
  62.  
  63. bool sidecountsread;            /* Obscure, fixes numbers of restored units */
  64.  
  65. /* Malloc just enough space for the map. */
  66.  
  67. allocate_map()
  68. {
  69.     world.terrain = (Hex *) malloc(world.width * world.height * sizeof(Hex));
  70. }
  71.  
  72. /* Game files can live in library directories or somewhere else.  This */
  73. /* function tries to find a file, open it, and load the contents. */
  74.  
  75. load_mapfile(name)
  76. char *name;
  77. {
  78.     bool loaded = FALSE;
  79.     int i;
  80.     FILE *fp = NULL;
  81.  
  82.     make_pathname(XCONQLIB, name, "", spbuf);
  83.     for (i = 0; i < nummaps; ++i) {
  84.     if (strcmp(name,  mapfilenames[i]) == 0) { loaded = TRUE; break; }
  85.     if (strcmp(spbuf, mapfilenames[i]) == 0) { loaded = TRUE; break; }
  86.     }
  87.     if (loaded) {
  88.     fprintf(stderr, "\"%s\" is already loaded.\n", name);
  89.     } else if ((fp = fopen(spbuf, "r")) != NULL) {
  90.     if (Debug) printf("Reading \"%s\" ...\n", spbuf);
  91.     mapfilenames[nummaps++] = copy_string(spbuf);
  92.     } else if ((fp = fopen(name, "r")) != NULL) {
  93.     if (Debug) printf("Reading \"%s\" ...\n", name);
  94.     mapfilenames[nummaps++] = copy_string(name);
  95.     } else {
  96.     fprintf(stderr, "Neither \"%s\" or \"%s\" could be opened!\n",
  97.         spbuf, name);
  98.     exit(1);
  99.     }
  100.     if (fp != NULL) {
  101.     load_sections(fp);
  102.     fclose(fp);
  103.     }
  104.     if (periodmade) {
  105.     perfilename = name;
  106.     periodmade = FALSE;
  107.     }
  108. }
  109.  
  110. /* Grab up all the components of a mapfile, and recurse if any submapfiles. */
  111. /* The header line tells us which sections to try to load.  For some */
  112. /* sections, flag them as loaded so we know not to synthesize replacements. */
  113.  
  114. load_sections(fp)
  115. FILE *fp;
  116. {
  117.     char sect[BUFSIZE], *tmp;
  118.     int numfiles, i;
  119.     Header *head;
  120.  
  121.     /* Read and interpret the header */
  122.     head = (Header *) malloc(sizeof(Header));
  123.     if (mainhead == NULL) mainhead = head;
  124.     fscanf(fp, "Xconq %d %s", &numfiles, sect);
  125.     head->numincls = numfiles;
  126.     head->sections = copy_string(sect);
  127.     head->sdetail = head->udetail = 0;
  128.     tmp = read_line(fp);
  129.     head->notes[0] = tmp;
  130.     head->numnotes = 1;
  131.     if (tmp[strlen(tmp)-1] == ';') {
  132.     for (i = 1; i < MAXMAPNOTES; ++i) {
  133.         head->notes[i] = read_line(fp);
  134.         if (strcmp(head->notes[i], ".") == 0) {
  135.         head->numnotes = i;
  136.         break;
  137.         }
  138.         if (Debug) printf("%s\n", head->notes[i]);
  139.     }
  140.     }
  141.     for (i = 0; i < numfiles; ++i) {
  142.     head->includes[i] = read_line(fp);
  143.     if (Debug) printf("Including %s ...\n", head->includes[i]);
  144.     load_mapfile(head->includes[i]);
  145.     }
  146.     sidecountsread = FALSE;
  147.     /* Suck in all the sections */
  148.     if (vsect(sect)) {
  149.     read_version(fp);
  150.     }
  151.     if (psect(sect)) {
  152.     read_period(fp);
  153.     periodmade = TRUE;
  154.     }
  155.     if (msect(sect)) {
  156.     read_map(fp);
  157.     mapmade = TRUE;
  158.     }
  159.     if (gsect(sect)) {
  160.     read_globals(fp);
  161.     globalsmade = TRUE;
  162.     }
  163.     if (ssect(sect)) {
  164.     head->sdetail = read_sides(fp);
  165.     sidesmade = TRUE;
  166.     }
  167.     if (usect(sect)) {
  168.     head->udetail = read_units(fp);
  169.     unitsmade = TRUE;
  170.     }
  171.     sidecountsread = FALSE;
  172. }
  173.  
  174. /* Read the version line and test it against the program version. */
  175. /* Continuation after failure is likely to result in core dump, so leave. */
  176.  
  177. read_version(fp)
  178. FILE *fp;
  179. {
  180.     char *tmpver;
  181.  
  182.     if (Debug) printf("Will try to read version ...\n");
  183.     tmpver = read_line(fp);
  184.     if (strcmp(tmpver, version) != 0) {
  185.     fprintf(stderr, "Sorry, mapfile is for some other version!\n");
  186.     fprintf(stderr, "Version should be \"%s\", not \"%s\".\n",
  187.         tmpver, version);
  188.     exit(1);
  189.     }
  190.     if (Debug) printf("... Done reading version.\n");
  191. }
  192.  
  193. /* Read the map section, starting with header, then working through data. */
  194. /* The map data may be partly run-length encoded; this is recognized by the */
  195. /* presence of digit strings (of arbitrary length) followed by the run char. */
  196. /* Any or all of the data may be so encoded, otherwise the chars are 1-1 */
  197. /* with map hexes; newlines are always at the end of each map row. */
  198.  
  199. read_map(fp)
  200. FILE *fp;
  201. {
  202.     char ch;
  203.     int extension, x, y, terr, run;
  204.  
  205.     fscanf(fp, "Map %d %d %d %d %d\n",
  206.        &world.width, &world.height,
  207.        &world.scale, &world.known, &extension);
  208.     if (Debug) printf("Will try to read %dx%d map ...\n",
  209.               world.width, world.height);
  210.     allocate_map();
  211.     for (y = world.height-1; y >= 0; --y) {
  212.     for (x = 0; x < world.width; /* incr below */) {
  213.         ch = getc(fp);
  214.         if (isdigit(ch)) {
  215.         run = ch - '0';
  216.         while (isdigit(ch = getc(fp))) {
  217.             run = run * 10 + ch - '0';
  218.         }
  219.         } else {
  220.         run = 1;
  221.         }
  222.         terr = find_terr(ch);
  223.         if (terr < 0) terr = period.defaultterrain;
  224.         if (terr < 0) {
  225.         fprintf(stderr, "'%c' is not valid terrain in this period!\n",
  226.             ch);
  227.         exit(1);
  228.         }
  229.         while (run-- > 0) {
  230.         set_terrain_at(x, y, terr);
  231.         set_people_at(x, y, NOBODY);
  232.         set_unit_at(x, y, NULL);
  233.         ++x;
  234.         }
  235.     }
  236.     fscanf(fp, "\n");
  237.     }
  238.     if (Debug) printf("... Done reading map.\n");
  239. }
  240.  
  241. /* This should be more efficient.  Fortunately, run-length encoded maps */
  242. /* don't call it for every single hex! */
  243.  
  244. find_terr(ch)
  245. char ch;
  246. {
  247.     register int t;
  248.  
  249.     for_all_terrain_types(t) if (ch == ttypes[t].tchar) return t;
  250.     return (-1);
  251. }
  252.  
  253. /* Period reading is complicated, and therefore lives in its own file. */
  254.  
  255. /* Read the globals section.  Most info of interest is in the header, but */
  256. /* win/lose conditions need extra reads. */
  257.  
  258. read_globals(fp)
  259. FILE *fp;
  260. {
  261.     int extension, i, u, r;
  262.     Condition *cond;
  263.  
  264.     fscanf(fp, "Globals %d %d %d %d %d %d\n",
  265.        &global.time, &global.endtime, &global.setproduct,
  266.        &global.leavemap, &global.numconds, &extension);
  267.     if (Debug) printf("Will try to read globals ...\n");
  268.     for (i = 0; i < global.numconds; ++i) {
  269.     cond = &(global.winlose[i]);
  270.     fscanf(fp, "%d %d %d %d %d\n",
  271.            &(cond->win), &(cond->type),
  272.            &(cond->starttime), &(cond->endtime), &(cond->sidesn));
  273.     switch (cond->type) {
  274.     case TERRITORY:
  275.         fscanf(fp, "%d\n", &(cond->n));
  276.         break;
  277.     case UNITCOUNT:
  278.         for_all_unit_types(u) {
  279.         fscanf(fp, "%d", &(cond->units[u]));
  280.         }
  281.         fscanf(fp, "\n");
  282.         break;
  283.     case RESOURCECOUNT:
  284.         for_all_resource_types(r) {
  285.         fscanf(fp, "%d", &(cond->resources[r]));
  286.         }
  287.         fscanf(fp, "\n");
  288.         break;
  289.     case POSSESSION:
  290.         fscanf(fp, "%d %d %d\n", &(cond->x), &(cond->y), &(cond->n));
  291.         break;
  292.     default:
  293.         case_panic("condition type", cond->type);
  294.         break;
  295.     }
  296.     }
  297.     if (Debug) printf("... Done reading globals.\n");
  298. }
  299.  
  300. /* Read in the entire sides section.  Sides have several levels of detail, */
  301. /* since sides' views of the world can be pretty large and we don't always */
  302. /* need to remember them. */
  303.  
  304. int sidedetail;
  305.  
  306. read_sides(fp)
  307. FILE *fp;
  308. {
  309.     int numtoread, detail, extension, i;
  310.     Side *side;
  311.  
  312.     curgiven = 0;
  313.     fscanf(fp, "Sides %d %d %d\n", &numtoread, &detail, &extension);
  314.     if (detail < 0 || detail > SIDEALL) case_panic("detail", detail);
  315.     sidedetail = detail;
  316.     if (Debug) printf("Will try to read %d sides at detail %d ...\n",
  317.               numtoread, detail);
  318.     for (i = 0; i < numtoread; ++i) {
  319.     if (detail >= SIDEBASIC) side = read_basic_side(fp);
  320.     if (side != NULL) {
  321.         if (detail >= SIDESLOTS) read_side_attributes(side, numtoread, fp);
  322.         if (detail >= SIDEVIEW)  read_side_view(side, fp);
  323.         if (detail >= SIDEMISC) {
  324.         read_side_misc(side, fp);
  325.         read_side_statistics(side, fp);
  326.         }
  327.         if (Debug) printf("Got side named \"%s\",\n", side->name);
  328.     }
  329.     }
  330.     if (Debug) printf("... Done reading sides.\n");
  331.     return detail;
  332. }
  333.  
  334. /* Create a side, which only requires a name - person-ness and display come */
  335. /* from the command line if any were supplied. */
  336.  
  337. /* Somebody should react coherently to side creation failure... */
  338.  
  339. Side *
  340. read_basic_side(fp)
  341. FILE *fp;
  342. {
  343.     bool human = FALSE;
  344.     char *host = NULL;
  345.     int j;
  346.  
  347.     fscanf(fp, "%s\n", tmpbuf);
  348.     for (j = 0; j < strlen(tmpbuf); ++j) if (tmpbuf[j] == '*') tmpbuf[j] = ' ';
  349.     if (curgiven < numgivens) {
  350.     human = humans[curgiven];
  351.     host = hosts[curgiven];
  352.     ++curgiven;
  353.     }
  354.     return create_side(tmpbuf, human, host);
  355. }
  356.  
  357. /* Read the most important attributes of a side. */
  358.  
  359. read_side_attributes(side, numtoread, fp)
  360. Side *side;
  361. int numtoread;
  362. FILE *fp;
  363. {
  364.     int s, u;
  365.  
  366.     fscanf(fp, "%hd", &(side->ideology));
  367.     for (s = 0; s < numtoread; ++s) fscanf(fp, "%hd", &(side->attitude[s]));
  368.     for_all_unit_types(u) fscanf(fp, "%hd", &(side->counts[u]));
  369.     fscanf(fp, "\n");
  370.     sidecountsread = TRUE;
  371. }
  372.  
  373. /* Read the goriest of details about a side - those things that are */
  374. /* relevant only to a particular game. */
  375.  
  376. read_side_misc(side, fp)
  377. Side *side;
  378. FILE *fp;
  379. {
  380.     bool human;
  381.     char host[BUFSIZE];
  382.  
  383.     fscanf(fp, "%d %s %hd ",
  384.        &human, host, &(side->lost));
  385.     fscanf(fp, "\n");
  386. }
  387.  
  388. /* Read the performance statistics of a side. */
  389.  
  390. read_side_statistics(side, fp)
  391. Side *side;
  392. FILE *fp;
  393. {
  394.     int u, u2, i;
  395.  
  396.     for_all_unit_types(u) {
  397.     for (i = 0; i < NUMREASONS; ++i) {
  398.         fscanf(fp, "%hd", &(side->balance[u][i]));
  399.     }
  400.     fscanf(fp, "\n");
  401.     for_all_unit_types(u2) {
  402.         fscanf(fp, "%hd", &(side->atkstats[u][u2]));
  403.     }
  404.     fscanf(fp, "\n");
  405.     for_all_unit_types(u2) {
  406.         fscanf(fp, "%hd", &(side->hitstats[u][u2]));
  407.     }
  408.     fscanf(fp, "\n");
  409.     }
  410. }
  411.  
  412. /* Read about what has been seen in the world. */
  413.  
  414. read_side_view(side, fp)
  415. Side *side;
  416. FILE *fp;
  417. {
  418.     char ch1, ch2;
  419.     int x, y, view;
  420.  
  421.     for (y = 0; y < world.height; ++y) {
  422.     for (x = 0; x < world.width; ++x) {
  423.         ch1 = getc(fp);  ch2 = getc(fp);
  424.         if (ch1 == '?' && ch2 == '?')
  425.         view = UNSEEN;
  426.         else if (ch1 == '.' && ch2 == '.')
  427.         view = EMPTY;
  428.         else
  429.         view = buildview(ch2 - '0', find_unit_char(ch1));
  430.         set_side_view(side, x, y, view);
  431.     }
  432.     fscanf(fp, "\n");
  433.     }
  434. }
  435.  
  436. /* Read the entire units section.  Units also have different levels of */
  437. /* detail. */
  438.  
  439. read_units(fp)
  440. FILE *fp;
  441. {
  442.     int numtoread, detail, extension, i;
  443.     Unit *unit, *transport;
  444.  
  445.     fscanf(fp, "Units %d %d %d\n", &numtoread, &detail, &extension);
  446.     if (detail < 0 || detail > UNITALL) case_panic("detail", detail);
  447.     if (Debug) printf("Will try to restore %d units at detail %d ...\n",
  448.               numtoread, detail);
  449.     for (i = 0; i < numtoread; ++i) {
  450.     nut = -1;
  451.     if (detail >= UNITBASIC) {
  452.         if ((unit = read_basic_unit(fp)) != NULL) {
  453.         if (detail >= UNITSLOTS) read_unit_attributes(unit, fp);
  454.         if (detail >= UNITORDERS) read_unit_orders(unit, fp);
  455.         if (nusn >= 0) {
  456.             unit_changes_side(unit, side_n(nusn), -1, -1);
  457.         }
  458.         if (nut >= 0) {
  459.             transport = find_unit(nut);
  460.             if (transport != NULL) {
  461.             occupy_unit(unit, transport);
  462.             } else {
  463.             occupy_hex(unit, nux, nuy);
  464.             }
  465.         } else {
  466.             occupy_hex(unit, nux, nuy);
  467.         }
  468.         if (Debug) printf("Got %s,\n", unit_handle(NULL, unit));
  469.         }
  470.     }
  471.     }
  472.     if (Debug) printf("... Done reading units.\n");
  473.     return detail;
  474. }
  475.  
  476. /* Read the barest info about a neutral unit - just type, name, and loc. */
  477. /* Do weird stuff to handle empty names represented by "*". */
  478. /* Give it full supplies (good idea?).  Can't place just yet, because it */
  479. /* might actually be an occupant of something else. */
  480.  
  481. Unit *
  482. read_basic_unit(fp)
  483. FILE *fp;
  484. {
  485.     char ch;
  486.     int j, u;
  487.     Unit *newunit;
  488.  
  489.     fscanf(fp, "%c %s %d,%d %d\n", &ch, tmpbuf, &nux, &nuy, &nusn);
  490.     for (j = 0; j < strlen(tmpbuf); ++j) if (tmpbuf[j] == '*') tmpbuf[j] = ' ';
  491.     if (strcmp(tmpbuf, " ") == 0) strcpy(tmpbuf, "");
  492.     if ((u = find_unit_char(ch)) != NOTHING) {
  493.     if ((newunit = create_unit(u, tmpbuf)) != NULL) {
  494.         init_supply(newunit);
  495.     }
  496.     } else {
  497.     fprintf(stderr, "Unit '%c' is not of this period!\n", ch);
  498.     exit(1);
  499.     }
  500.     return newunit;
  501. }
  502.  
  503. /* Read most of a unit's attributes, but not orders and suchlike. */
  504.  
  505. read_unit_attributes(unit, fp)
  506. Unit *unit;
  507. FILE *fp;
  508. {
  509.     int truesidenum, r;
  510.  
  511.     fscanf(fp, "%hd %hd %d %hd %hd %hd %hd %hd %hd %hd %d",
  512.        &(unit->id), &(unit->number), &truesidenum,
  513.        &(unit->hp), &(unit->quality), &(unit->morale), &(unit->fatigue),
  514.        &(unit->product), &(unit->schedule), &(unit->built), &nut);
  515.     for_all_resource_types(r) {
  516.     fscanf(fp, "%hd", &(unit->supply[r]));
  517.     }
  518.     fscanf(fp, "\n");
  519.     unit->trueside = side_n(truesidenum);
  520.     /* tricky way to ensure subsequent units will have unique ids */
  521.     nextid = max(nextid, unit->id + 1);
  522. }
  523.  
  524. /* Read everything back in and try to recreate the unit's orders exactly. */
  525. /* The saved orders include a flag for the presence of standing orders. */
  526.  
  527. read_unit_orders(unit, fp)
  528. Unit *unit;
  529. FILE *fp;
  530. {
  531.     int i, more, uord[MAXUTYPES];
  532.  
  533.     fscanf(fp, "%hd %hd", &(unit->lastdir), &(unit->awake));
  534.     read_one_order(fp, &(unit->orders));
  535.     fscanf(fp, "%d\n", &more);
  536.     if (more) {
  537.     unit->standing = (StandingOrder *) malloc(sizeof(StandingOrder));
  538.     for_all_unit_types(i) {
  539.         fscanf(fp, "%d", &(uord[i]));
  540.     }
  541.     fscanf(fp, "\n");
  542.     for_all_unit_types(i) {
  543.         if (uord[i]) {
  544.         unit->standing->orders[i] = (Order *) malloc(sizeof(Order));
  545.         read_one_order(fp, unit->standing->orders[i]);
  546.         fscanf(fp, "\n");
  547.         }
  548.     }
  549.     }
  550. }
  551.  
  552. /* The exact way to read an order depends on what type it is. */
  553.  
  554. read_one_order(fp, order)
  555. FILE *fp;
  556. Order *order;
  557. {
  558.     int leadernum;
  559.  
  560.     fscanf(fp, "%hd %hd %hd", &(order->type), &(order->rept), &(order->flags));
  561.     switch (orderargs[order->type]) {
  562.     case NOARG:
  563.     break;
  564.     case DIR:
  565.     fscanf(fp, "%hd", &(order->p.dir));
  566.     break;
  567.     case POS:
  568.     fscanf(fp, "%hd,%hd", &(order->p.pt[0].x), &(order->p.pt[0].y));
  569.     break;
  570.     case LEADER:
  571.     fscanf(fp, "%d", &leadernum);
  572.     /* should finish this */
  573.     break;
  574.     case WAYPOINTS:
  575.     fscanf(fp, "%hd,%hd %hd,%hd",
  576.            &(order->p.pt[0].x), &(order->p.pt[0].y),
  577.            &(order->p.pt[1].x), &(order->p.pt[1].y));
  578.     break;
  579.     default:
  580.     case_panic("order arg type", orderargs[order->type]);
  581.     break;
  582.     }
  583. }
  584.  
  585. /* Output is quite similar to input of mapfiles, but of course everything */
  586. /* is reversed.  */
  587.  
  588. /* A savefile has a particular format; it includes all sections except */
  589. /* period, which may or may not be referenced as an included file. */
  590. /* It is important that this routine not attempt to use graphics, since it */
  591. /* may be called when graphics code fails. */
  592.  
  593. write_savefile(fname)
  594. char *fname;
  595. {
  596.     Header *head;
  597.  
  598.     head = (Header *) malloc(sizeof(Header));
  599.     head->numincls = (perfilename ? 1 : 0);
  600.     head->includes[0] = perfilename;
  601.     head->sections = "+-++++";
  602.     head->numnotes = 0;
  603.     head->sdetail = SIDEALL;
  604.     head->udetail = UNITALL;
  605.     return write_mapfile(fname, head);
  606. }
  607.  
  608. /* A scenario is considerably more variable than a savefile, but the */
  609. /* principle is the same. */
  610.  
  611. write_scenario(fname, sections, sdetail, udetail)
  612. char *fname, *sections;
  613. int sdetail, udetail;
  614. {
  615.     int i;
  616.     Header *head;
  617.  
  618.     head = (Header *) malloc(sizeof(Header));
  619.     head->numincls = head->numnotes = 0;
  620.     if (mainhead) {
  621.     head->numnotes = mainhead->numnotes;
  622.     for (i = 0; i < head->numnotes; ++i) {
  623.         head->notes[i] = mainhead->notes[i];
  624.     }
  625.     head->numincls = mainhead->numincls;
  626.     for (i = 0; i < head->numincls; ++i) {
  627.         head->includes[i] = mainhead->includes[i];
  628.     }
  629.     }
  630.     head->sections = copy_string(sections);
  631.     head->sdetail = sdetail;
  632.     head->udetail = udetail;
  633.     return write_mapfile(fname, head);
  634. }
  635.  
  636. /* Given a file name and a header telling what to put in, do the putting. */
  637. /* Returns true if file opened successfully. */
  638.  
  639. write_mapfile(fname, head)
  640. char *fname;
  641. Header *head;
  642. {
  643.     int i;
  644.     FILE *fp;
  645.  
  646.     if ((fp = fopen(fname, "w")) != NULL) {
  647.     fprintf(fp, "Xconq %d %s %s%s\n",
  648.         head->numincls, head->sections,
  649.         (head->numnotes > 0 ? head->notes[0] : ""),
  650.         (head->numnotes > 1 ? ";" : ""));
  651.     if (head->numnotes > 1) {
  652.         for (i = 1; i < head->numnotes; ++i) {
  653.         fprintf(fp, "%s\n", head->notes[i]);
  654.         }
  655.         fprintf(fp, ".\n");
  656.     }
  657.     for (i = 0; i < head->numincls; ++i) {
  658.         fprintf(fp, "%s\n", head->includes[i]);
  659.     }
  660.     if (vsect(head->sections)) write_version(fp);
  661.     /* no period writing */
  662.     if (msect(head->sections)) write_map(fp);
  663.     if (gsect(head->sections)) write_globals(fp);
  664.     if (ssect(head->sections)) write_sides(fp, head->sdetail);
  665.     if (usect(head->sections)) write_units(fp, head->udetail);
  666.     fclose(fp);
  667.     return TRUE;
  668.     } else {
  669.     return FALSE;
  670.     }
  671. }
  672.  
  673. /* Writing out the version is pretty easy. */
  674.  
  675. write_version(fp)
  676. FILE *fp;
  677. {
  678.     fprintf(fp, "%s\n", version);
  679. }
  680.  
  681. /* Write the map section.  Try to find runs of the same type and make a more */
  682. /* compact output. */
  683.  
  684. write_map(fp)
  685. FILE *fp;
  686. {
  687.     int extension = 0, x, y, run, runterr, terr, i;
  688.  
  689.     fprintf(fp, "Map %d %d %d %d %d\n",
  690.         world.width, world.height, world.scale, world.known, extension);
  691.     for (y = world.height-1; y >= 0; --y) {
  692.     run = 0;
  693.     runterr = terrain_at(0, y);
  694.     for (x = 0; x < world.width; ++x) {
  695.         terr = terrain_at(x, y);
  696.         if (terr == runterr) {
  697.         run++;
  698.         } else {
  699.         if (run >= 3) {
  700.             fprintf(fp, "%d%c", run, ttypes[runterr].tchar);
  701.         } else {
  702.             for (i = 0; i < run; ++i) putc(ttypes[runterr].tchar, fp);
  703.         }
  704.         runterr = terr;
  705.         run = 1;
  706.         }
  707.     }
  708.     fprintf(fp, "%d%c\n", run, ttypes[terr].tchar);
  709.     }
  710. }
  711.  
  712. /* Write the globals section, which mostly consists of win/lose conditions. */
  713.  
  714. write_globals(fp)
  715. FILE *fp;
  716. {
  717.     int extension = 0, i, u, r;
  718.     Condition *cond;
  719.  
  720.     fprintf(fp, "Globals %d %d %d %d %d %d\n",
  721.         global.time, global.endtime, global.setproduct,
  722.         global.leavemap, global.numconds, extension);
  723.     for (i = 0; i < global.numconds; ++i) {
  724.     cond = &(global.winlose[i]);
  725.     fprintf(fp, "%d %d %d %d %d\n",
  726.         cond->win, cond->type,
  727.         cond->starttime, cond->endtime, cond->sidesn);
  728.     switch (cond->type) {
  729.     case TERRITORY:
  730.         fprintf(fp, "%d\n", cond->n);
  731.         break;
  732.     case UNITCOUNT:
  733.         for_all_unit_types(u) {
  734.         fprintf(fp, "%d ", cond->units[u]);
  735.         }
  736.         fprintf(fp, "\n");
  737.         break;
  738.     case RESOURCECOUNT:
  739.         for_all_resource_types(r) {
  740.         fprintf(fp, "%d ", cond->resources[r]);
  741.         }
  742.         fprintf(fp, "\n");
  743.         break;
  744.     case POSSESSION:
  745.         fprintf(fp, "%d %d %d\n", cond->x, cond->y, cond->n);
  746.         break;
  747.     default:
  748.         case_panic("condition type", cond->type);
  749.         break;
  750.     }
  751.     }
  752. }
  753.  
  754. /* Write the sides section, at the given level of detail. */
  755.  
  756. write_sides(fp, detail)
  757. FILE *fp;
  758. int detail;
  759. {
  760.     int extension = 0;
  761.     Side *side;
  762.  
  763.     fprintf(fp, "Sides %d %d %d\n", numsides, detail, extension);
  764.     if (detail < 0 || detail > SIDEALL) case_panic("detail", detail);
  765.     if (Debug) printf("Will try to write %d sides at detail %d ...\n",
  766.               numsides, detail);
  767.     for_all_sides(side) {
  768.     if (detail >= SIDEBASIC) write_basic_side(side, fp);
  769.     if (detail >= SIDESLOTS) write_side_attributes(side, fp);
  770.     if (detail >= SIDEVIEW)  write_side_view(side, fp);
  771.     if (detail >= SIDEMISC) {
  772.         write_side_misc(side, fp);
  773.         write_side_statistics(side, fp);
  774.     }
  775.     if (Debug) printf("Wrote side \"%s\",\n", side->name);
  776.     }
  777.     if (Debug) printf("... Done writing sides.\n");
  778. }
  779.  
  780. /* A side can be saved with or without the entire view, which is a fairly */
  781. /* large sort of thing.  Machine sides without displays have their names */
  782. /* written as "*" - let us hope and pray that such a perverted name never */
  783. /* appears as a valid X host/display name. */
  784.  
  785. write_basic_side(side, fp)
  786. Side *side;
  787. FILE *fp;
  788. {
  789.     int j;
  790.  
  791.     strcpy(tmpbuf, side->name);
  792.     for (j = 0; j < strlen(tmpbuf); ++j) if (tmpbuf[j] == ' ') tmpbuf[j] = '*';
  793.     fprintf(fp, "%s\n", tmpbuf);
  794. }
  795.  
  796. /* Write the random but important attributes of a side. */
  797.  
  798. write_side_attributes(side, fp)
  799. Side *side;
  800. FILE *fp;
  801. {
  802.     int u, s;
  803.  
  804.     fprintf(fp, "%d  ", side->ideology);
  805.     for (s = 0; s < numsides; ++s) fprintf(fp, "%d ", side->attitude[s]);
  806.     fprintf(fp, " ");
  807.     for_all_unit_types(u) fprintf(fp, "%d ", side->counts[u]);
  808.     fprintf(fp, "\n");
  809. }
  810.  
  811. /* Write about what has been seen in the world. (should be more compact) */
  812.  
  813. write_side_view(side, fp)
  814. Side *side;
  815. FILE *fp;
  816. {
  817.     char ch1, ch2;
  818.     int x, y, view;
  819.  
  820.     for (y = 0; y < world.height; ++y) {
  821.     for (x = 0; x < world.width; ++x) {
  822.         view = side_view(side, x, y);
  823.         if (view == UNSEEN)
  824.         ch1 = ch2 = '?';
  825.         else if (view == EMPTY)
  826.         ch1 = ch2 = '.';
  827.         else {
  828.         ch1 = utypes[vtype(view)].uchar;
  829.         ch2 = vside(view) + '0';
  830.         }
  831.         putc(ch1, fp);  putc(ch2, fp);
  832.     }
  833.     fprintf(fp, "\n");
  834.     }
  835. }
  836.  
  837. /* More volatile things, generally only of interest for saved games. */
  838.  
  839. write_side_misc(side, fp)
  840. Side *side;
  841. FILE *fp;
  842. {
  843.     fprintf(fp, "%d %s %d \n",
  844.         side->humanp, (side->host ? side->host : "*"),
  845.         side->lost, side->timeleft);
  846. }
  847.  
  848. /* Write the performance statistics of a side. (the unit record may be */
  849. /* crucial to deciding win/lose conditions.) */
  850.  
  851. write_side_statistics(side, fp)
  852. Side *side;
  853. FILE *fp;
  854. {
  855.     int u, u2, i;
  856.  
  857.     for_all_unit_types(u) {
  858.     for (i = 0; i < NUMREASONS; ++i) {
  859.         fprintf(fp, "%d ", side->balance[u][i]);
  860.     }
  861.     fprintf(fp, "\n");
  862.     for_all_unit_types(u2) {
  863.         fprintf(fp, "%d ", side->atkstats[u][u2]);
  864.     }
  865.     fprintf(fp, "\n");
  866.     for_all_unit_types(u2) {
  867.         fprintf(fp, "%d ", side->hitstats[u][u2]);
  868.     }
  869.     fprintf(fp, "\n");
  870.     }
  871. }
  872.  
  873. /* Write the unit section of a mapfile.  Each level of detail has its own */
  874. /* line, while standing orders will take one line per unit type. */
  875. /* Get rid of dead units and sort everything, so as to make sure that */
  876. /* transports are always written before occupants. */
  877.  
  878. write_units(fp, detail)
  879. FILE *fp;
  880. int detail;
  881. {
  882.     int extension = 0;
  883.     Unit *unit;
  884.  
  885.     flush_dead_units();
  886.     sort_units(TRUE);
  887.     if (detail < 0 || detail > UNITALL) case_panic("detail", detail);
  888.     fprintf(fp, "Units %d %d %d\n", numunits, detail, extension);
  889.     if (Debug) printf("Writing %d units at detail %d ...\n", numunits, detail);
  890.     if (detail >= UNITBASIC) {
  891.     for_all_units(unit) {
  892.         if (detail >= UNITBASIC) write_basic_unit(unit, fp);
  893.         if (detail >= UNITSLOTS) write_unit_attributes(unit, fp);
  894.         if (detail >= UNITORDERS) write_unit_orders(unit, fp);
  895.         if (Debug) printf("Wrote %s,\n", unit_handle(NULL, unit));
  896.     }
  897.     }
  898.     if (Debug) printf("... Done writing units.\n");
  899. }
  900.  
  901. /* Write only the minimal info about a unit - type, name, and position. */
  902. /* To make scanf happy, spaces in the name are replaced with stars. */
  903. /* Fortunately, names never seem to have stars in them. */
  904. /* Unnamed units get a star all by itself in the name position. */
  905.  
  906. write_basic_unit(unit, fp)
  907. Unit *unit;
  908. FILE *fp;
  909. {
  910.     int j;
  911.  
  912.     if (unit->name == NULL || strlen(unit->name) < 1) {
  913.     sprintf(tmpbuf, "*");
  914.     } else {
  915.     strcpy(tmpbuf, unit->name);
  916.     for (j = 0; j < strlen(tmpbuf); ++j)
  917.         if (tmpbuf[j] == ' ') tmpbuf[j] = '*';
  918.     }
  919.     fprintf(fp, "%c %s %d,%d %d\n",
  920.         utypes[unit->type].uchar, tmpbuf, unit->x, unit->y,
  921.         side_number(unit->side));
  922. }
  923.  
  924. /* Write the most interesting attributes of a unit.  Just a long list of */
  925. /* numbers, no special tricks needed. */
  926.  
  927. write_unit_attributes(unit, fp)
  928. Unit *unit;
  929. FILE *fp;
  930. {
  931.     int i;
  932.  
  933.     fprintf(fp, "%d %d %d  %d  %d %d %d  %d %d %d  %d  ",
  934.         unit->id, unit->number, side_number(unit->trueside), 
  935.         unit->hp, unit->quality, unit->morale, unit->fatigue,
  936.         unit->product, unit->schedule, unit->built,
  937.         (unit->transport ? unit->transport->id : -1));
  938.     for_all_resource_types(i) {
  939.     fprintf(fp, "%d ", unit->supply[i]);
  940.     }
  941.     fprintf(fp, "\n");
  942. }
  943.  
  944. /* Write the unit's orders and standing orders out. */
  945. /* This is usually for game saving, although I suppose it has other uses. */
  946.  
  947. write_unit_orders(unit, fp)
  948. Unit *unit;
  949. FILE *fp;
  950. {
  951.     int i;
  952.  
  953.     fprintf(fp, "%d  %d ", unit->lastdir, unit->awake);
  954.     write_one_order(fp, &(unit->orders));
  955.     fprintf(fp, " %d\n", (unit->standing != NULL));
  956.     if (unit->standing != NULL) {
  957.     for_all_unit_types(i) {
  958.         fprintf(fp, "%d ", (unit->standing->orders[i] != NULL));
  959.     }
  960.     fprintf(fp, "\n");
  961.     for_all_unit_types(i) {
  962.         if (unit->standing->orders[i] != NULL) {
  963.         write_one_order(fp, unit->standing->orders[i]);
  964.         fprintf(fp, "\n");
  965.         }
  966.     }
  967.     }
  968. }
  969.  
  970. /* Write a single order object, which may be for a unit or a standing order. */
  971.  
  972. write_one_order(fp, order)
  973. FILE *fp;
  974. Order *order;
  975. {
  976.     fprintf(fp, " %d %d %d ", order->type, order->rept, order->flags);
  977.     switch (orderargs[order->type]) {
  978.     case NOARG:
  979.     break;
  980.     case DIR:
  981.     fprintf(fp, "%d", order->p.dir);
  982.     break;
  983.     case POS:
  984.     fprintf(fp, "%d,%d", order->p.pt[0].x, order->p.pt[0].y);
  985.     break;
  986.     case LEADER:
  987.     fprintf(fp, "%d", order->p.leader->id);
  988.     break;
  989.     case WAYPOINTS:
  990.     fprintf(fp, "%d,%d %d,%d",
  991.         order->p.pt[0].x, order->p.pt[0].y,
  992.         order->p.pt[1].x, order->p.pt[1].y);
  993.     break;
  994.     default:
  995.     case_panic("order arg type", orderargs[order->type]);
  996.     break;
  997.     }
  998. }
  999.  
  1000. /* Display the mapfile header info. */
  1001.  
  1002. describe_mapfiles(side)
  1003. Side *side;
  1004. {
  1005.     int i;
  1006.  
  1007.     wprintf(side, "The world is %d hexes around by %d hexes high.",
  1008.         world.width, world.height);
  1009.     wprintf(side, "");
  1010.     if (mainhead != NULL && mainhead->numnotes > 0) {
  1011.     for (i = 0; i < mainhead->numnotes; ++i) {
  1012.         wprintf(side, "%s", mainhead->notes[i]);
  1013.     }
  1014.     } else {
  1015.     wprintf(side, "(No other documentation is available.)");
  1016.     }
  1017. }
  1018.  
  1019. /* Generalized area search routine.  It starts in the immediately adjacent */
  1020. /* hexes and expands outwards.  The basic structure is to examine successive */
  1021. /* "rings" out to the max distance; within each ring, we must scan each of */
  1022. /* six faces (picking a random one to start with) by iterating along that */
  1023. /* face, in a direction 120 degrees from the direction out to one corner of */
  1024. /* the face.  Draw a picture if you want to understand it... */
  1025.  
  1026. /* Note that points far outside the map may be generated, but the predicate */
  1027. /* will not be called on them.  It may be applied to the same point several */
  1028. /* times, however. */
  1029.  
  1030. search_area(x0, y0, maxdist, pred, rxp, ryp)
  1031. int x0, y0, maxdist, (*pred)(), *rxp, *ryp;
  1032. {
  1033.     int clockwise, dist, dd, d, dir, x1, y1, i, dir2, x, y;
  1034.  
  1035.     maxdist = max(min(maxdist, world.width), min(maxdist, world.height));
  1036.     clockwise = (flip_coin() ? 1 : -1);
  1037.     for (dist = 1; dist <= maxdist; ++dist) {
  1038.     dd = random_dir();
  1039.     for_all_directions(d) {
  1040.         dir = (d + dd) % NUMDIRS;
  1041.         x1 = x0 + dist * dirx[dir];
  1042.         y1 = y0 + dist * diry[dir];
  1043.         for (i = 0; i < dist; ++i) {
  1044.         dir2 = opposite_dir(dir + clockwise);
  1045.         x = x1 + i * dirx[dir2];
  1046.         y = y1 + i * diry[dir2];
  1047.         if (between(0, y, world.height-1)) {
  1048.             if ((*pred)(wrap(x), y)) {
  1049.             *rxp = wrap(x);  *ryp = y;
  1050.             return TRUE;
  1051.             }
  1052.         }
  1053.         }
  1054.     }
  1055.     }
  1056.     return FALSE;
  1057. }
  1058.  
  1059. /* Apply a function to every hex within the given radius, being careful (for */
  1060. /* both safety and efficiency reasons) not to go past edges.  Note that the */
  1061. /* distance is inclusive, and that distance of 0 means x0,y0 only. */
  1062. /* This routine should be avoided in time-critical code. */
  1063.  
  1064. apply_to_area(x0, y0, dist, fn)
  1065. int x0, y0, dist, (*fn)();
  1066. {
  1067.     int x, y, x1, y1, x2, y2;
  1068.  
  1069.     dist = min(dist, max(world.width, world.height));
  1070.     y1 = y0 - dist;
  1071.     y2 = y0 + dist;
  1072.     for (y = y1; y <= y2; ++y) {
  1073.     if (between(0, y, world.height-1)) {
  1074.         x1 = x0 - (y < y0 ? (y - y1) : dist);
  1075.         x2 = x0 + (y > y0 ? (y2 - y) : dist);
  1076.         for (x = x1; x <= x2; ++x) {
  1077.         ((*fn)(wrap(x), y));
  1078.         }
  1079.     }
  1080.     }
  1081. }
  1082.  
  1083. #ifdef SCO_UNIX
  1084. int
  1085. windex(x,y)
  1086. {
  1087.     return (world.width * y + x);
  1088. }
  1089. #endif
  1090.